'use strict';

var entryFactory = require('../../../../factory/EntryFactory');

var cmdHelper = require('../../../../helper/CmdHelper'),
    eventDefinitionHelper = require('../../../../helper/EventDefinitionHelper'),
    utils = require('../../../../Utils');

var getBusinessObject = require('bpmn-js/lib/util/ModelUtil').getBusinessObject,
    is = require('bpmn-js/lib/util/ModelUtil').is;

var forEach = require('lodash/forEach'),
    find = require('lodash/find'),
    filter = require('lodash/filter');


function getContainedActivities(element) {
    return getFlowElements(element, 'bpmn:Activity');
}

function getContainedBoundaryEvents(element) {
    return getFlowElements(element, 'bpmn:BoundaryEvent');
}

function getFlowElements(element, type) {
    return utils.filterElementsByType(element.flowElements, type);
}

function isCompensationEventAttachedToActivity(activity, boundaryEvents) {
    var activityId = activity.id;
    var boundaryEvent = find(boundaryEvents, function (boundaryEvent) {
        var compensateEventDefinition = eventDefinitionHelper.getCompensateEventDefinition(boundaryEvent);
        var attachedToRef = boundaryEvent.attachedToRef;
        return compensateEventDefinition && attachedToRef && attachedToRef.id === activityId;
    });
    return !!boundaryEvent;
}

// subprocess: only when it is not triggeredByEvent
// activity: only when it attach a compensation boundary event
// callActivity: no limitation
function canActivityBeCompensated(activity, boundaryEvents) {
    return (is(activity, 'bpmn:SubProcess') && !activity.triggeredByEvent) ||
        is(activity, 'bpmn:CallActivity') ||
        isCompensationEventAttachedToActivity(activity, boundaryEvents);
}

function getActivitiesForCompensation(element) {
    var boundaryEvents = getContainedBoundaryEvents(element);
    return filter(getContainedActivities(element), function (activity) {
        return canActivityBeCompensated(activity, boundaryEvents);
    });
}

function getActivitiesForActivityRef(element) {
    var bo = getBusinessObject(element);
    var parent = bo.$parent;

    var activitiesForActivityRef = getActivitiesForCompensation(parent);

    // if throwing compensation event is in an event sub process:
    // get also all activities outside of the event sub process
    if (is(parent, 'bpmn:SubProcess') && parent.triggeredByEvent) {
        parent = parent.$parent;
        if (parent) {
            activitiesForActivityRef = activitiesForActivityRef.concat(getActivitiesForCompensation(parent));
        }

    }

    return activitiesForActivityRef;
}

function createActivityRefOptions(element) {
    var options = [{value: ''}];

    var activities = getActivitiesForActivityRef(element);
    forEach(activities, function (activity) {
        var activityId = activity.id;
        var name = (activity.name ? (activity.name + ' ') : '') + '(id=' + activityId + ')';
        options.push({value: activityId, name: name});
    });

    return options;
}


module.exports = function (group, element, bpmnFactory, compensateEventDefinition, elementRegistry, translate) {

    group.entries.push(entryFactory.checkbox({
        id: 'wait-for-completion',
        label: translate('Wait for Completion'),
        modelProperty: 'waitForCompletion',

        get: function (element, node) {
            return {
                waitForCompletion: compensateEventDefinition.waitForCompletion
            };
        },

        set: function (element, values) {
            values.waitForCompletion = values.waitForCompletion || false;
            return cmdHelper.updateBusinessObject(element, compensateEventDefinition, values);
        }
    }));

    group.entries.push(entryFactory.selectBox({
        id: 'activity-ref',
        label: translate('Activity Ref'),
        selectOptions: createActivityRefOptions(element),
        modelProperty: 'activityRef',

        get: function (element, node) {
            var activityRef = compensateEventDefinition.activityRef;
            activityRef = activityRef && activityRef.id;
            return {
                activityRef: activityRef || ''
            };
        },

        set: function (element, values) {
            var activityRef = values.activityRef || undefined;
            activityRef = activityRef && getBusinessObject(elementRegistry.get(activityRef));
            return cmdHelper.updateBusinessObject(element, compensateEventDefinition, {
                activityRef: activityRef
            });
        }
    }));

};
